<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>图片碎片化展示</title>
    <style>
      body {
        width: 100%;
        height: 100vh;
        padding: 0;
        margin: 0;
        display: flex;
        justify-content: center;
        align-items: center;
      }
      .box {
        /* 宽高和背景图保持一致,否则可能会出现白色间隙无法消除的问题 */
        width: 468px;
        height: 703px;
        display: flex;
        /* 小块自动换行排列 */
        flex-wrap: wrap;
        justify-content: center;
      }
      .small-box {
        background-image: url("https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/99b070fcb1de471d9af4f4d5d3f71909~tplv-k3u1fbpfcp-jj-mark:0:0:0:0:q75.image#?w=1120&h=1680&s=2088096&e=png&b=d098d0");
        box-sizing: border-box;
        background-repeat: no-repeat;
        opacity: 0;
        animation: smallBoxAnimate 2000ms linear forwards;
        --rotateX: rotateX(0);
        --rotateY: rotateY(0);
        transform: var(--rotateX) var(--rotateY) scale(0.8);
      }

      @keyframes smallBoxAnimate {
        0% {
          opacity: 0;
          transform: var(--rotateX) var(--rotateY) scale(0.8);
        }
        40% {
          opacity: 0;
          transform: var(--rotateX) var(--rotateY) scale(0.8);
        }
        70% {
          opacity: 1;
          transform: rotateX(0) rotateY(0) scale(0.8);
        }
        100% {
          opacity: 1;
          transform: rotateX(0) rotateY(0) scale(1);
        }
      }
    </style>
  </head>
  <body>
    <!-- 
    参考笔记 : https://juejin.cn/post/7379856289487831074#heading-0
    原理是：通过容器/图片大小生成一定数量的小块，然后每个小块背景也使用相同图片，
    再使用 background-size 与 background-position 属性调整背景图片的大小与位置，
    使小块又合成一整张大图片，这操作和使用"精灵图"的操作是一样的，最后，我们再给每个小块增加动画效果，就大功告成。
   -->
    <div id="box" class="box"></div>

    <div style="margin-left: 40px">
      <button data-index="1" class="boxButton">至上而下</button>
      <br /><br />
      <button data-index="2" class="boxButton">从左至右</button>
      <br /><br />
      <button data-index="3" class="boxButton">随机</button>
      <br /><br />
      <button data-index="4" class="boxButton">从左上角到右下角</button>
      <br /><br />
      <button data-index="5" class="boxButton">从中心向四周扩散</button>
      <br /><br />
      <button data-index="6" class="boxButton">从四周向中心聚齐</button>
      <br /><br />
      <button data-index="7" class="boxButton">相邻两个小块旋转相反</button>
    </div>

    <script>
      document.addEventListener("DOMContentLoaded", () => {
        // 添加按钮点击事件
        document.querySelectorAll(".boxButton").forEach(function (button) {
          button.addEventListener("click", function () {
            var index = this.getAttribute("data-index");
            document.getElementById("box").innerHTML = "";
            createSmallBox(index);
          });
        });
        const box = document.getElementById("box");
        const { width, height } = box.getBoundingClientRect();
        // 定义多少个小块，由多少行和列决定
        const row = 14;
        const col = 10;
        // 计算小块的宽高
        const smallBoxWidth = width / col;
        const smallBoxHeight = height / row;
        /** @name 创建小块 **/
        function createSmallBox(index) {
          for (let i = 0; i < row; i++) {
            for (let j = 0; j < col; j++) {
              const smallBox = document.createElement("div");
              smallBox.classList.add("small-box");
              smallBox.style.width = smallBoxWidth + "px";
              smallBox.style.height = smallBoxHeight + "px";
              //   smallBox.style.border = "1px solid red";
              // 设置背景偏移量，让小块的背景显示对应图片的位置，和以前那种精灵图一样
              const offsetX = j * smallBoxWidth * -1;
              const offsetY = i * smallBoxHeight * -1;
              smallBox.style.backgroundPosition = `${offsetX}px ${offsetY}px`;
              smallBox.style.backgroundSize = `${width}px ${height}px`;
              // 延迟时间为毫秒(ms)，注意不要太小了
              var delay = 0;
              if (index == 1) {
                //  至上而下
                delay = i * 100;
              } else if (index == 2) {
                // 从左到右
                delay = j * 100;
              } else if (index == 3) {
                // 随机
                const getRandom = (min, max) =>
                  Math.floor(Math.random() * (max - min + 1) + min);
                delay = getRandom(0, col + row) * 100;
              } else if (index == 4) {
                // 从左上角到右下角
                delay = (i + j) * 100;
              } else if (index == 5) {
                // 从中心向四周扩散
                delay = (Math.abs(col / 2 - j) + Math.abs(row / 2 - i)) * 100;
              } else if (index == 6) {
                // 从四周向中心聚齐
                delay =
                  (col / 2 -
                    Math.abs(col / 2 - j) +
                    (col / 2 - Math.abs(row / 2 - i))) *
                  100;
              } else if (index == 7) {
                //  至上而下
                delay = i * 100;
                // 相邻两个小块旋转相反
                const contrary = (i + j) % 2 === 0;
                smallBox.style.setProperty(
                  "--rotateX",
                  `rotateX(${contrary ? -180 : 0}deg)`
                );
                smallBox.style.setProperty(
                  "--rotateY",
                  `rotateY(${contrary ? 0 : -180}deg)`
                );
              }
              // 给每个小块增加不同的延时，让动画不同时间执行
              smallBox.style.animationDelay = `${delay}ms`;

              // 解决相邻背景图片白条/白边间隙问题：
              smallBox.style.willChange = "transform";
              // 在动画执行后，需要重置will-change
              const timer = setTimeout(() => {
                smallBox.style.willChange = "initial";
                clearTimeout(timer);
              }, 2000);

              box.appendChild(smallBox);
            }
          }
        }
        createSmallBox(1);
      });
    </script>
  </body>
</html>
